package se.mbaeumer.fxtodo.core.main;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;

import javax.xml.stream.XMLStreamException;

import net.fortuna.ical4j.data.ParserException;

import se.mbaeumer.fxtodo.core.handler.ContextHandler;
import se.mbaeumer.fxtodo.core.handler.ICSInfoHandler;
import se.mbaeumer.fxtodo.core.handler.PriorityHandler;
import se.mbaeumer.fxtodo.core.handler.TaskHandler;
import se.mbaeumer.fxtodo.core.handler.TaskStatusHandler;
import se.mbaeumer.fxtodo.core.handler.TaskTypeHandler;
import se.mbaeumer.fxtodo.core.model.Context;
import se.mbaeumer.fxtodo.core.model.EnumICSInfoType;
import se.mbaeumer.fxtodo.core.model.EnumPriority;
import se.mbaeumer.fxtodo.core.model.EnumTaskStatus;
import se.mbaeumer.fxtodo.core.model.ICSInfo;
import se.mbaeumer.fxtodo.core.model.Task;
import se.mbaeumer.fxtodo.core.model.TaskStatus;
import se.mbaeumer.fxtodo.core.util.constants.ContextConstants;
import se.mbaeumer.fxtodo.core.util.dataimport.ICSReader;
import se.mbaeumer.fxtodo.core.util.dataimport.XMLReader;
import se.mbaeumer.fxtodo.db.DatabaseHandler;

public class FXTodoMain {
	private static final FXTodoMain INSTANCE = new FXTodoMain();
	private FXTodoMain(){this.initHandlers();};
	public static FXTodoMain getInstance(){return INSTANCE;}
	
	private ContextHandler contextHandler;
	private TaskHandler taskHandler;
	private TaskStatusHandler taskStatusHandler;
	private PriorityHandler priorityHandler;
	private TaskTypeHandler taskTypeHandler;
	private ICSInfoHandler icsInfoHandler;
	private DatabaseHandler dbHandler;
	
	private void initHandlers(){
		this.contextHandler = ContextHandler.getInstance();
		this.taskHandler = TaskHandler.getInstance();
		this.taskStatusHandler = TaskStatusHandler.getInstance();
		this.priorityHandler = PriorityHandler.getInstance();
		this.taskTypeHandler = TaskTypeHandler.getInstance();
		this.icsInfoHandler = ICSInfoHandler.getInstance();
		this.dbHandler = DatabaseHandler.getInstance();
	}
	
	
	// context handling
	public void createContext() {

	}

	/**
	 * Updates a given context
	 * @param context
	 * @throws SQLException
	 */
	public void updateContext(Context context) throws SQLException {
		this.contextHandler.updateContext(this.dbHandler,context);
	}

	/**
	 * Deletes a context with a given id
	 * @param contextId the id of the context
	 * @param isCascading indicates if the tasks shall be deleted as well
	 */
	public void deleteContext(int contextId, boolean isCascading) {
		if (isCascading){
			// delete also the tasks in this context
			this.taskHandler.deleteTasksFromContext(this.dbHandler, contextId);
			
		}else{
			//move the tasks first (set the context to 'undefined')
			List<Task> tasksInContext = this.taskHandler.getTasksByContext(this.dbHandler, contextId);
			for (Task t : tasksInContext){
				this.taskHandler.moveTask(this.dbHandler, t, -1);
			}
		}
		
		this.contextHandler.deleteContexts(this.dbHandler, contextId);
	}
	
	/**
	 * Gets all contexts
	 * @return
	 */
	public  List<Context> getAllContexts(){
		return this.contextHandler.loadContextsFromDB(this.dbHandler);
	}
	
	/**
	 * Loads the context titles from the database
	 * @return
	 */
	public List<String> loadContextTitlesFromDB(){
		return this.contextHandler.getContextTitles(this.dbHandler);
	}
	
	/**
	 * Create a dummy object of type Context
	 * @return
	 */
	public Context getDummyContext(){
		return this.contextHandler.getDummyContext();
	}

	// task handling
	public void createTask() {

	}

	/**
	 * Updates a given task
	 * @param task
	 */
	public void updateTask(Task task, String contextTitle) {
		
		// get the contextId of the task
		int contextId=-1;
		if (task.getTaskContext() != null){
			contextId = this.contextHandler.getContextIdByTitle(this.dbHandler, task.getTaskContext().getTitle());		
		}else{
			contextId = this.contextHandler.getContextIdByTitle(this.dbHandler, contextTitle);		
		}
		// get the statusId of the task
		int statusId = this.taskStatusHandler.getStatusIdByTitle(this.dbHandler, task.getStatus().toString());
		int priorityId = this.priorityHandler.getPriorityIdByTitle(this.dbHandler, task.getPriority().toString());
		this.taskHandler.updateTask(this.dbHandler, task, contextId, statusId, priorityId);
	}

	public void deleteTask(int taskId) {
		this.taskHandler.deleteTask(this.dbHandler, taskId);
	}
	/**
	 * Gets all currently existing tasks
	 * @return
	 */
	public List<Task> getAllTasks(){
		return this.taskHandler.getTasks(this.dbHandler);
	}
	
	public List<Task> getTasks(String contextTitle, String period, String status, String priority){
		int contextId = -1;
		if (contextTitle.equals(ContextConstants.CONTEXT_ALL)){
			contextId = -2;
		}else{
			contextId = this.contextHandler.getContextIdByTitle(this.dbHandler, contextTitle);
		}
		int statusId = -1;
		statusId = this.taskStatusHandler.getStatusIdByTitle(this.dbHandler, status);
		
		int priorityId = -1;
		priorityId = this.priorityHandler.getPriorityIdByTitle(this.dbHandler, priority);
		return this.taskHandler.getTasks(this.dbHandler, contextId, period, statusId, priorityId);
	}
	
	public Context getContextByID(int id){
		return this.contextHandler.getContext(this.dbHandler, id);
	}
	
	public EnumTaskStatus getStatusByTitle(String title){
		int statusId = this.taskStatusHandler.getStatusIdByTitle(this.dbHandler, title);
		return this.taskStatusHandler.getStatusById(this.dbHandler,statusId);
	}
	
	
	/**
	 * Creates a dummy object of type Task
	 * @return
	 */
	public Task getDummyTask(){
		return this.taskHandler.createDummyTask();
	}
	
	// task status handling
	public void createTaskStatus() {

	}

	public void updateTaskStatus() {

	}

	public void deleteTaskStatus() {

	}
	
	/**
	 * Gets all task statuses
	 * @return
	 */
	public List<TaskStatus> getTaskStatuses(){
		return this.taskStatusHandler.getTaskStatuses(this.dbHandler);
	}

	// task type handling
	public void createTaskType() {

	}

	public void updateTaskType() {

	}

	public void deleteTaskType() {

	}
	
	/**
	 * Moves an existing task to another context
	 * @param task the selected task
	 * @param newContext the title of the context 
	 */
	public void moveTask(Task task, String newContext){
		// get the context
		int contextId = -1;
		contextId = this.contextHandler.getContextIdByTitle(this.dbHandler,newContext);
		
		this.taskHandler.moveTask(this.dbHandler, task, contextId);
	}
	
	public List<String> getPriorityTitles(){
		return this.priorityHandler.getPriorityTitles(this.dbHandler);
	}
	
	public EnumPriority getPriorityByTitle(String title){
		int priorityId = this.priorityHandler.getPriorityIdByTitle(this.dbHandler, title);
		return this.priorityHandler.getPriorityById(this.dbHandler, priorityId);
	}
	
	public void importFromXML(String filename) throws FileNotFoundException, XMLStreamException, SQLException{
		this.prepareImport();
		XMLReader xmlReader = new XMLReader(this, filename);
		xmlReader.readDataFromFile();
		for (Context c : xmlReader.getContexts()){
			this.contextHandler.importContext(this.dbHandler, c);
		}
		for (Task t : xmlReader.getTasks()){
			this.taskHandler.importTaskFromXML(this.dbHandler, t);
		}
		
		for (ICSInfo ics : xmlReader.getIcsInfos()){
			this.icsInfoHandler.importICSInfoFromXML(this.dbHandler, ics);
		}
	}
	
	private void prepareImport(){
		this.taskHandler.deleteTasksFromContext(this.dbHandler, -1);
		this.contextHandler.deleteContexts(this.dbHandler, -1);
		this.icsInfoHandler.deleteICSInfos(this.dbHandler);
	}
	
	public void importTaskFromICS(String filename) throws IOException, ParserException, SQLException{
		ICSReader icsReader = new ICSReader(filename);
		icsReader.readFromFile();
		for (ICSInfo ics : icsReader.getIcsInfos()){
			Task task = new Task.TaskBuilder().
					title(ics.getsummary()).
					description(ics.getDescription()).
					created(new Date()).
					dueDate(ics.getStartTime()).
					build();
			// add the task to the database
			int taskId = this.taskHandler.importTaskFromICS(this.dbHandler, task, new Integer(ics.getPriorityString()));
			
			// now add the icsinfo to the database
			ics.setTaskId(taskId);
			this.icsInfoHandler.createICSInfo(this.dbHandler, ics);
		}
	}
	
	public List<ICSInfo> getICSInfos(){
		return this.icsInfoHandler.getICSInfo(this.dbHandler);
	}
	
	public ICSInfo getICSInfoByTaskID(int taskId){
		return this.icsInfoHandler.getICSInfoByTaskID(this.dbHandler, taskId);
	}
	
	public EnumICSInfoType getICSInfoTypeByName(String name){
		return this.icsInfoHandler.getICSInfoTypeByName(this.dbHandler, name);
	}
}
